//////////////////////////////////////////////////////////////////////////
// PeekPocket - the hassle-free WiFi scanner for Windows Mobile
// coded by dzolee
// http://dzolee.blogspot.com
//
// This is a custom-drawn list control
//////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "PeekPocket.h"
#include "ListCtrlEx.h"
#include "TextCache.h"
#include "DataStorage.h"
#include "SettingsStorage.h"
#include "WifiPeek.h"

extern CTextCache g_Texts;
extern CDataStorage g_APData;
extern CSettingsStorage g_Settings;
extern CWifiPeek g_WifiPeek;

// CListCtrlEx
IMPLEMENT_DYNAMIC(CListCtrlEx, CListCtrl)

//////////////////////////////////////////////////////////////////////////
CListCtrlEx::CListCtrlEx()
{
	m_bmp10.LoadBitmap(IDB_STAR_10);
	m_bmp14.LoadBitmap(IDB_STAR_14);
	m_bmp18.LoadBitmap(IDB_STAR_18);
}

//////////////////////////////////////////////////////////////////////////
CListCtrlEx::~CListCtrlEx()
{
}

BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl)
	ON_WM_LBUTTONDOWN()
	ON_NOTIFY_REFLECT(NM_RECOGNIZEGESTURE, OnRecognizeGesture)
	ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
END_MESSAGE_MAP()

// CListCtrlEx message handlers
void CListCtrlEx::OnLButtonDown(UINT nFlags, CPoint point)
{
	//CWnd::OnLButtonDown would call SHRecognizeGesture which would pop up the dots
	Default();
}

//////////////////////////////////////////////////////////////////////////
void CListCtrlEx::OnRecognizeGesture(NMHDR* pNMHDR, LRESULT* pResult)
{
	//do nothing
	*pResult = TRUE;
} 

//////////////////////////////////////////////////////////////////////////
void CListCtrlEx::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
NMLVCUSTOMDRAW* pLVCD;
int idx;
DWORD dwFontSize, dwTmp;

	pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR);

	//do default processing after this
	*pResult = CDRF_DODEFAULT;

	// First thing - check the draw stage. If it's the control's prepaint
	// stage, then tell Windows we want messages for every item.
	switch(pLVCD->nmcd.dwDrawStage)
	{
		case CDDS_PREPAINT:
		{
			*pResult = CDRF_NOTIFYITEMDRAW;
			break;
		}
		case CDDS_ITEMPREPAINT:
		{
			// This is the notification message for an item.  We'll request
			// notifications before each subitem's prepaint stage.

			*pResult = CDRF_NOTIFYSUBITEMDRAW;

			//lookup info struct for coloring
			idx=static_cast<int>(pLVCD->nmcd.dwItemSpec);
			if(idx != -1)
			{
				g_APData.GetFilteredItemByIndex(idx, &m_diTmp);
			}
			break;
		}
		case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
		{
			COLORREF crTxt, crBkg;

			crBkg=GetSysColor(COLOR_WINDOW);
			crTxt=GetSysColor(COLOR_WINDOWTEXT);

			//if BSSID is not open, "Name" and "Sec" will be printed in red
			if(pLVCD->iSubItem == 0 || pLVCD->iSubItem == 4)
			{
				if(m_diTmp.Auth != Ndis802_11AuthModeOpen)
				{
					crTxt=RGB(255, 0, 0);
				}
			}

			//inactive entries greyed out
			if(m_diTmp.bActive == false)
			{
				crTxt=GetSysColor(COLOR_GRAYTEXT);
			}

			//get font size;
			g_Settings.GetNumeric(nseFontSize, dwFontSize);

			//get item rect
			CRect rc;
			rc=pLVCD->nmcd.rc;

#ifdef _DEBUG
			{
				CString str;
				str.Format(_T("Font size is %d, rect is {(%d, %d), (%d, %d)}\n"), dwFontSize, rc.left, rc.top, rc.right, rc.bottom);
				OutputDebugString(str);
			}
#endif

			//subitem-dependent graphic drawing
			switch(pLVCD->iSubItem)
			{
				case 0: /* AP name */
				{
					//draw star for currently preferred AP
					BYTE CurrentMac[6];

					//get MAC add of preferred AP
					memset((void *)CurrentMac, 0, sizeof(CurrentMac));
					CString strCurrentAdapter;

					g_Settings.GetString(sseAdapter, strCurrentAdapter);
					g_WifiPeek.GetPreferredMACAddress(strCurrentAdapter.GetBuffer(), &CurrentMac[0]);

					if(memcmp((void *)CurrentMac, (void *)m_diTmp.BSSID, 6) == 0)
					{
						//the current item is the preferred AP, draw a little star before the name
						if(dwFontSize < 14)
						{
							dwTmp=10;	
							m_bmp10.Display(CDC::FromHandle(pLVCD->nmcd.hdc), rc.left, rc.top+(dwFontSize-dwTmp)/2);
						}
						else
						if(dwFontSize < 17)
						{
							dwTmp=14;
							m_bmp14.Display(CDC::FromHandle(pLVCD->nmcd.hdc), rc.left, rc.top+(dwFontSize-dwTmp)/2);
						}
						else
						{
							dwTmp=18;
							m_bmp18.Display(CDC::FromHandle(pLVCD->nmcd.hdc), rc.left, rc.top+(dwFontSize-dwTmp)/2);
						}

						//shift list item rect to the right, name will be drawn there
						pLVCD->nmcd.rc.left += 2+dwTmp;
					}//if this item is preferred AP
					break;
				} /* AP name */
				case 1: /* signal strenght */
				{
					//draw sort of bar diagram for active items
					if(m_diTmp.bActive == true)
					{
						//width of gfx equals to font size
						rc.right = rc.left + 2 + dwFontSize;	//bars rect

						//shift list item rect to the right, text will be drawn there
						pLVCD->nmcd.rc.left += 2+dwFontSize;

						//draw mini graph
						DrawMiniBars(pLVCD->nmcd.hdc, rc, dwFontSize, m_diTmp.RSSI);
					} //if active
					break;
				} /* signal strenght */
			}

			pLVCD->clrText=crTxt;
			pLVCD->clrTextBk=crBkg;

			// Tell Windows to paint the control itself.
			*pResult = CDRF_DODEFAULT;
			break;
		}//case subitem prepaint
	}//switch drawstage
} 

//////////////////////////////////////////////////////////////////////////
//draw small bar diagram
void CListCtrlEx::DrawMiniBars(HDC hDC, CRect rc, DWORD dwSize, int iSig)
{
CPen green_pen, dgray_pen, lgray_pen;
HGDIOBJ hOldPen;
int w, h, x, startx, starty, separators, threshold;
double d1, d2, d3;

	w=rc.Width()-2;
	h=dwSize-2;

	startx=rc.left + 1;
	starty=rc.bottom - 1;

	green_pen.CreatePen(PS_SOLID, 1, RGB(50, 255, 50));
	dgray_pen.CreatePen(PS_SOLID, 1, RGB(150, 150, 150));
	lgray_pen.CreatePen(PS_SOLID, 1, RGB(220, 220, 220));

	//threshold is where green lines turn to gray.
	//it will be zero for signals below 90, and should be "w" for signals above 10.
	d1=(double)abs(iSig);

	if(d1 < 10)
	{
		threshold = w;
	}
	else
	if(d1 > 90)
	{
		threshold = 0;
	}
	else
	{
		//is there no rounding in stdlib??
		//round value
		d1 = w - (w*((d1-10) / 80));
		//get fractional portion
		d2 = modf(d1, &d3);
		//integer part
		d1=d3;
		//if fraction not less than one half then round up
		if(d2 >= 0.5)
		{
			d1++;
		}
		threshold = (int)d1;
	}
	
#ifdef _DEBUG
	{
		CString str;
		str.Format(_T("Signal: %d, width: %d px, threshold: %d px\n"), iSig, w, threshold);
		OutputDebugString(str);
	}
#endif

	//separator locations
	if(dwSize < 14)
	{
		separators = 3;
	}
	else
	if(dwSize < 17)
	{
		separators = 4;
	}
	else
	{
		separators = 5;
	}

	//initial pen
	if(threshold == 0)
	{
		//very small signal, start with gray pen
		hOldPen=SelectObject(hDC, lgray_pen);
	}
	else
	{
		hOldPen=SelectObject(hDC, green_pen);
	}

	for(x=0; x<w; x++)
	{
		if(x == threshold)
		{
			//switch to gray pen
			hOldPen=SelectObject(hDC, lgray_pen);
		}
		MoveToEx(hDC, startx+x, starty, NULL);
		if(x > 0 && !(x % separators) && x != w-1)
		{
			SelectObject(hDC, dgray_pen);
			LineTo(hDC, startx+x, starty-x);

			if(x > threshold)
			{
				//gray pen
				hOldPen=SelectObject(hDC, lgray_pen);
			}
			else
			{
				//green pen
				SelectObject(hDC, green_pen);
			}
		}
		else
		{
			LineTo(hDC, startx+x, starty-x);
		}
	}// for x
	SelectObject(hDC, hOldPen);
}
